using System;
using System.Collections.Generic;
using MicroStar.Patterns;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MicroStar
{
    public class ParticleEffect
    {
        private static readonly Random random = new Random();
        
        private readonly List<Texture2D> textures = new List<Texture2D>();
        private readonly List<IModifier> modifiers = new List<IModifier>();
        private readonly Queue<Particle> freeParticles = new Queue<Particle>(); 
        private readonly Particle[] particles;

        private IPattern emissionPattern;
        
        private Color particleColor = Color.White;
        private int particleLifespan;

        private int activeParticles;
        private int highestActiveCount;

        private int emitAmount = 5;
        private float particleSpeed = 1f;

        public ParticleEffect(int maxParticles, int particleLifespan)
        {
            this.particleLifespan = particleLifespan;
            particles = new Particle[maxParticles];

            for (int i = 0; i < particles.Length; i++)
            {
                particles[i] = new Particle();
                freeParticles.Enqueue(particles[i]);
            }
        }

        public void Emit(GameTime gameTime, Vector2 position)
        {
            if (textures.Count == 0)
                throw new InvalidOperationException("Error emitting particles - no textures have be specified.");

            float totalMilliseconds = (float) gameTime.TotalGameTime.TotalMilliseconds;
            int particleCount = (int)MathHelper.Clamp(random.Next((int)(emitAmount * 0.8f), emitAmount), 1, particles.Length);

            for (int i = 0; i < particleCount && freeParticles.Count > 0; i++)
            {
                Particle newParticle = freeParticles.Dequeue();
                Vector2 emitPosition = position;

                if(emissionPattern != null)
                {
                    emitPosition = emissionPattern.CalculateParticlePosition(random, position);
                }

                Texture2D texture = textures[random.Next(textures.Count)];
                newParticle.Initialize(texture, emitPosition, particleColor, particleLifespan, totalMilliseconds);

                Vector2 velocity = new Vector2((float)random.NextDouble() * particleSpeed, 0);
                float angle = MathHelper.ToRadians(random.Next(360));
                newParticle.Velocity = Vector2.Transform(velocity, Matrix.CreateRotationZ(angle));
            }
        }

        public void Update(GameTime gameTime)
        {
            for (int i = 0; i < particles.Length; i++)
            {
                if (particles[i].IsAlive)
                {
                    float particleAge = (float)(gameTime.TotalGameTime.TotalMilliseconds - particles[i].InceptionTime) / particles[i].Lifespan;

                    for (int j = 0; j < Modifiers.Count; j++)
                        Modifiers[j].Update(particleAge, gameTime.TotalGameTime.TotalMilliseconds, gameTime.ElapsedGameTime.TotalSeconds, particles[i]);

                    particles[i].Update(gameTime.TotalGameTime.TotalMilliseconds);
                    activeParticles++;

                    if (!particles[i].IsAlive)
                        freeParticles.Enqueue(particles[i]);
                }
            }

            if (activeParticles > highestActiveCount)
                highestActiveCount = activeParticles;

            activeParticles = 0;
        }

        // TODO: Refactor particle effect updating and drawing code.  Try not to loop through the whole array

        public void Draw(SpriteBatch spriteBatch)
        {
            for (int i = 0; i < particles.Length; i++)
            {
                if (particles[i].IsAlive)
                    particles[i].Draw(spriteBatch);
            }
        }

        public IPattern EmissionPattern
        {
            get { return emissionPattern; }
            set { emissionPattern = value; }
        }

        public bool IsDead
        {
            get { return freeParticles.Count == particles.Length; }
        }

        public int ParticleCount
        {
            get { return particles.Length; }
        }

        public int EmitAmount
        {
            get { return emitAmount; }
            set { emitAmount = value; }
        }

        public float MaxParticleSpeed
        {
            get { return particleSpeed; }
            set { particleSpeed = value; }
        }

        public int ParticleLifespan
        {
            get { return particleLifespan; }
            set { particleLifespan = value; }
        }

        public Color ParticleColor
        {
            get { return particleColor; }
            set { particleColor = value; }
        }

        public List<Texture2D> Textures
        {
            get { return textures; }
        }

        public List<IModifier> Modifiers
        {
            get { return modifiers; }
        }

        public int HighestActiveCount
        {
            get { return highestActiveCount; }
        }
    }
}
